/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * Copyright (C) 2024-2025 Jim Gao <gaozhsoft@aliyun.com>
 *
 */
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/device/class.h>
#include <linux/device.h>
#include <linux/delay.h>

#include "char_drv.h"

static int chardrv_dev_major = -1;
static const char chardrv_dev_name[] = "chardrv_dev";
static struct class 	*chardrv_class 	= NULL;
static struct device	*chardrv_dev 	= NULL;

// 模拟设备状态和配置
static int device_status = 909;
static int device_config = 908;

// IOCTL 实现
static long char_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int ret = 0;

    switch (cmd) {
        case CHAR_DRV_IOCTL_RESET:
            // 重置设备
            device_status = 0;
            device_config = 0;
			printk("[%s +%d %s] Device reset\n", __FILE__, __LINE__, __func__);
            break;

        case CHAR_DRV_IOCTL_GET_STATUS:
            // 返回设备状态
			printk("[%s +%d %s] Device status: %d\n", __FILE__, __LINE__, __func__, device_status);
			printk("[%s +%d %s] Device device_config: %d\n", __FILE__, __LINE__, __func__, device_config);
			if (copy_to_user((int __user *)arg, &device_status, sizeof(device_status))) {
                ret = -EFAULT;
            }

            break;

        case CHAR_DRV_IOCTL_SET_CONFIG:
            // 设置设备配置
			if (copy_from_user(&device_config, (int __user *)arg, sizeof(device_config))) {
                ret = -EFAULT;
            }
			if(123 == device_config)
			{
				device_status = 1;
			}
			printk("[%s +%d %s] Device status: %d\n", __FILE__, __LINE__, __func__, device_status);
			printk("[%s +%d %s] Device device_config: %d\n", __FILE__, __LINE__, __func__, device_config);
            break;

        default:
            ret = -ENOTTY;  // 未知的 IOCTL 命令
            break;
    }

    return ret;
}

static int char_drv_open(struct inode *inode, struct file *file)
{
	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);

	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
	return 0;
}

static int char_drv_close(struct inode *inode, struct file *file)
{
	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);

	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
	return 0;
}

static const struct file_operations char_drv_fops = {
	.owner 		= THIS_MODULE,
	.open 		= char_drv_open,
	.release 	= char_drv_close,
	.unlocked_ioctl = char_drv_ioctl,
};

static int __init char_drv_init(void)
{
    int ret = 0;
    printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
    //printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
    chardrv_dev_major = register_chrdev(0, chardrv_dev_name, &char_drv_fops);
	if(chardrv_dev_major < 0)
	{
		ret = chardrv_dev_major;
		printk("[%s +%d %s] register_chrdev(%s) failed\n", __FILE__, __LINE__, __func__, chardrv_dev_name);
		goto err_register_chrdev;
	}
    chardrv_class = class_create(THIS_MODULE, chardrv_dev_name);
	if(IS_ERR(chardrv_class))
	{
		printk("[%s +%d %s] class_create(%s) failed\n", __FILE__, __LINE__, __func__, chardrv_dev_name);
		goto err_class_create;
	}

    chardrv_dev = device_create(chardrv_class, NULL, MKDEV(chardrv_dev_major, 0), NULL, chardrv_dev_name);
	if(IS_ERR(chardrv_class))
	{
		printk("[%s +%d %s] device_create(%s) failed\n", __FILE__, __LINE__, __func__, chardrv_dev_name);
		goto err_device_create;
	}


	printk("[%s +%d %s] end\n", __FILE__, __LINE__, __func__);
	return 0;
    device_destroy(chardrv_class, MKDEV(chardrv_dev_major, 0));
err_device_create:
	class_destroy(chardrv_class);
err_class_create:
	unregister_chrdev(chardrv_dev_major, chardrv_dev_name);
err_register_chrdev:
    return 0;
}

static void __exit char_drv_exit(void)
{
    device_destroy(chardrv_class, MKDEV(chardrv_dev_major, 0));
    class_destroy(chardrv_class);
    unregister_chrdev(chardrv_dev_major, chardrv_dev_name);
    printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
}

module_init(char_drv_init);
module_exit(char_drv_exit);

MODULE_AUTHOR("Jim Gao <gaozhsoft@aliyun.com>");
MODULE_DESCRIPTION("basic example for char driver");
MODULE_LICENSE("GPL v2");

